#include "mm/kalloc.h"
#include "lib/string.h"
#include "sched/proc.h"
#include "user.h"
#include "shell.h"

#define MAX_COMMAND_SIZE 1024

bool shell_enabled = false;         // 表示是否正在 shell 中 
char *current_path = "~/";          // TODO: 当前路径（目前没有文件系统）
char raw_cmd_str[MAX_COMMAND_SIZE]; // 命令字符串
environment env;                    // 环境变量

void env_insert(char *name, char *value) {
    // 如果已有 key 则更新值
    int n = env.size, i;
    // printf("env: try to insert <%s, %s>\n", name, value);
    for (i=0; i<n; i++) {
        if (strcmp(env.var[i].name, name) == 0) {
            printf("env: find key %s\n", name);
            strcpy(&env.var[i].value, value);
            return;
        }
    }
    // printf("env: can't find key %s\n", name);
    // 如果不存在 key 则插入新变量
    strcpy(&env.var[n].name, name);
    strcpy(&env.var[n].value, value);
    env.size++;
    // printf("env: finish insert <%s, %s>\n", name, value);
}

void env_delete(char *name) {
    // printf("env: try to delete key %s\n", name);
    int n = env.size, i, j;
    if (n == 0) {
        // 环境变量为空
        perror(env_var, "env is empty");
        return;
    }

    for (i=0; i<n; i++) {
        if (strcmp(env.var[i].name, name) == 0) {
            // printf("env: find key %s\n", name);
            // 用后面的环境变量值覆盖前一个
            for (j=i+1; j<n; i++, j++) {
                strcpy(&env.var[i].name, env.var[j].name);
                strcpy(&env.var[i].value, env.var[j].value);
            }
            env.size--;
            return;
        }
    }
}

void env_init() {
    // printf("env: init start\n");
    env.size = 0;
    env_insert("USER", "ROOT");
    env_insert("UID", "0");
    env_insert("PLV", "0");
    env_insert("HOME", "~");
    // printf("env: init finish\n");
    return;
}

void envdump() {
    int n = env.size, i;
    for (i=0; i<n; i++) {
        printf("%s=%s\n", env.var[i].name, env.var[i].value);
    }
}

void hanoi_move(int s, int t, int l) {
    int other = 6 - s - t;
    char peg[3] = {'A', 'B', 'C'};
    if (l > 1) {
        hanoi_move(s, other, l - 1);
    }
    printf("disk[%d]: %c ==> %c\n", l, peg[s-1], peg[t-1]);
    if (l > 1) {
        hanoi_move(other, t, l - 1);
    }
}

void hanoi(char *number) {
    int n;
    n = atoi(number);
    hanoi_move(1, 3, n);
}

void shell_on() {
    env_init();
    shell_enabled = true;
}

void shell_off() {
    shell_enabled = false;
}

// shell 入口
void shell_entry() {
    int n;
    
    while (shell_enabled) {
        memset(raw_cmd_str, '\0', MAX_COMMAND_SIZE);
        printf("flos_kernel:%s $ ", current_path);
        n = gets(raw_cmd_str, MAX_COMMAND_SIZE);
        if (n == MAX_COMMAND_SIZE) {
            perror(shell, "input overflowed");
            continue;
        }
        if (raw_cmd_str[0] == 0) {
            continue;
        }
        parse_command();
    }
}

// shell 命令解析
void parse_command() {
    int i, n = 0;
    char temp[MAX_COMMAND_SIZE]; 
    char *cmd_array[MAX_COMMAND_SIZE];

    memset(temp, '\0', MAX_COMMAND_SIZE);
    memset(cmd_array, '\0', MAX_COMMAND_SIZE);
    clean_command(raw_cmd_str, temp, strlen(raw_cmd_str));    
    split(temp, " ", cmd_array, &n);

    if (n == 0 && raw_cmd_str[0] != null) {
        n = 1;
        *cmd_array = raw_cmd_str;
    }
    
    if (strcmp(cmd_array[0], "echo") == 0 || strcmp(cmd_array[0], "printf") == 0) {
        // NOTE: 不支持转义字符
        for (i=1; i<n; i++) {
            printf("%s ", cmd_array[i]);
        }
        printf("\n");
    } else if (strcmp(cmd_array[0], "env") == 0) {
        envdump();
    } else if (strcmp(cmd_array[0], "ps") == 0) {
        procdump();
    } else if (strcmp(cmd_array[0], "free") == 0) {
        kmemdump();
    } else if (strcmp(cmd_array[0], "whoami") == 0) {
        printf("root\n");
    } else if (strcmp(cmd_array[0], "setenv") == 0 || strcmp(cmd_array[0], "set") == 0) {
        if (n == 1) {
            envdump();
        } else if (n >= 3) {
            env_insert(cmd_array[1], cmd_array[2]);
        }
    } else if (strcmp(cmd_array[0], "unset") == 0) {
        if (n >= 2) {
            env_delete(cmd_array[1]);
        }
    } else if (strcmp(cmd_array[0], "hanoi") == 0) {
        if (n >= 2) {
            hanoi(cmd_array[1]);
        }
    } else if (strcmp(cmd_array[0], "help") == 0) {
        printf("echo env free hanoi printf ps set setenv unset whoami\n");
    } else {
        printf("%s:", cmd_array[0]);
        for (i=1; i<n; i++) {
            printf(" %s", cmd_array[i]);
        }
        printf(" No such file or directory\n");
    }
}